#ifndef MESSAGE_HPP
#define MESSAGE_HPP

#include <iostream>
#include <string>
#include <ctime>
#include <boost/lexical_cast.hpp>

int StringToInt(std::string input)
{
	return atoi(input.c_str());
}

std::string IntToString(int input)
{
	return boost::lexical_cast<std::string, int>(input);
}

enum Connectiontype{
	CLIENT = 0 ,
	SENSOR = 1,
	SERVER = 2,
	Unknown
};

enum Type{
	REQ = 0,
	RES,
	ACK 
};
	
enum Action{
	BIND = 0,
	PULL = 1,
	PUSH = 2,
	SUBMIT = 3,
	ENTITY = 4,
	UPDATE
};
	
enum DataType{
	Temp	= 0,
	Traffic = 1,
	CSS		= 2
};

Connectiontype	GetConnectioType( std::string data )
{
	if( data == "Client" )
	{
		return CLIENT;
	}
	else if( data == "Sensor" )
	{
		return SENSOR;
	}
	else if( data == "Server" )
	{
		return SERVER;
	}

	return Unknown;
}

const int c_iMessageSize = 128;
class Message
{
//Type::Action::VecClock::Data::Value
	public:
		DataType	m_DataType;
		Action		m_action;
		Type		m_type;
		std::string	m_strSenderID;

		std::string ID;
		std::string data;

		std::string VecClock;
		//std::string m_strValue; //Conveys the value of the data type, for example

		bool		m_bAcknowledged;
		time_t		m_TimeStamp;//Last time the message was (re)sent.
		time_t		m_ackTime;
		time_t		m_SendTime;
		int			m_resndCount;

		Message();
		Message(Type, Action, DataType, std::string, std::string);
		void		UpdateResend();
		void		ResetResend();
		bool		IsTimedOut( int );
		bool		Decode( std::string );
		std::string Encode();
		std::string Tostring(std::string field);
};

std::string Message::Encode()
{//Server_1::REQ::UPDATEUNKNOWN::Temp::0::Server_1 now has new data !
	std::string result;
	result = "";
	result += m_strSenderID;
	result += "::";
	switch(m_type)
	{
		case REQ:
			result += "REQ";
			break;
		case RES:
			result += "RES";
			break;
		case ACK:
			result += "ACK";
			break;
		default:
			result += "UNKNOWN";
			break;
	}
	result += "::";
	switch(m_action)
	{
		case BIND:
			result += "BIND";
			break;
		case PULL:
			result += "PULL";
			break;
		case PUSH:
			result += "PUSH";
			break;
		case SUBMIT:
			result += "SUBMIT";
			break;
		case ENTITY:
			result+= "ENTITY";
			break;
		case UPDATE:
			result+= "UPDATE";
			break;
		default:
			result += "UNKNOWN";
			break;
	}
	result += "::";
	switch(m_DataType)
	{
		case Temp:
			result += "Temp";
			break;
		case Traffic:
			result += "Traffic";
			break;
		case CSS:
			result += "CSS";
			break;
		default:
			result += "Unknown";
			break;
	}
	result += "::" + ID + "::";
	result += data;
//	std::cout << result << std::endl;
//	if(result.length() < MessageSize)
//	{
//		for(int i = result.length(); i < MessageSize ; i ++) {
//			result += "_";
//		}
//	}

//	std::cout <<"Message Length BEFORE padding: "<<result.length() << std::endl;
	while( result.length() < c_iMessageSize )
	{
		result += "_";
	}
//	std::cout <<"Message Length AFTER padding: "<<result.length() << std::endl;
	return result;
}

bool Message::Decode( std::string encodedMessage) {
	int index0 = encodedMessage.find("::");
	int index1 = encodedMessage.find("::", index0 + 1);
	int index2 = encodedMessage.find("::", index1 + 1);
	int index3 = encodedMessage.find("::", index2 + 1);
	int index4 = encodedMessage.find("::", index3 + 1);
	if((index1 == (std::string::npos)) || (index2 == (std::string::npos)) || (index3 == (std::string::npos)) || (index4 == (std::string::npos))) {
		return false;
	}
	m_strSenderID = encodedMessage.substr( 0 , index0 );
	//std::cout<< "sender ID : "<<m_strSenderID;
	//std::string type = encodedMessage.substr(index0 + 1 , index1);
	std::string type = encodedMessage.substr(index0 + 2, index1 - index0 - 2);
	//std::cout<<"Type = " << type<<"\n";
	if(type == "REQ")
	{
		this->m_type = REQ;
	}
	else if(type == "RES") {
		this->m_type = RES;
	}
	else if(type == "ACK") {
		this->m_type = ACK;
	}
	std::string action = encodedMessage.substr(index1 + 2, index2 - index1 - 2);
	if(action == "BIND")
	{
		this->m_action = BIND;
	}
	else if(action == "PULL") {
		this->m_action = PULL;
	}
	else if(action == "PUSH") {
		this->m_action = PUSH;
	}
	else if(action == "SUBMIT") {
		this->m_action = SUBMIT;
	}
	else if(action == "ENTITY") {
			this->m_action = ENTITY;
		}
	else if(action == "UPDATE") {
			this->m_action = UPDATE;
		}

	std::string dataType = encodedMessage.substr(index2 + 2, index3 - index2 - 2);
	if(dataType == "Temp")
	{
		this->m_DataType = Temp;
	}
	else if(dataType == "Traffic") {
		this->m_DataType = Traffic;
	}
	else if(dataType == "CSS") {
		this->m_DataType = CSS;
	}
	std::string ID = encodedMessage.substr(index3 + 2, index4 - index3 - 2);
	this->ID = ID;
	std::string data = encodedMessage.substr(index4 + 2, encodedMessage.length() - index4 - 1);
	//Remove extra ____
	std::size_t idx = data.find_first_of( '_' );
	data = data.substr( 0, idx );
	this->data = data;
	return true;
}



Message::Message()
{
	m_resndCount	= 0 ;
	m_bAcknowledged = false;
	m_strSenderID	= "NOT_INIT";
	time( &m_TimeStamp );
}

void Message::UpdateResend()
{
	m_resndCount++;
	time( &m_TimeStamp );
}
void Message::ResetResend()
{
	m_resndCount = 0 ;
	time( &m_TimeStamp );
	time( &m_SendTime );
	m_bAcknowledged = false;
}


bool Message::IsTimedOut( int TimeOutDuration  )
{
	time_t now;
	time( &now );
	double diff = difftime( now, m_TimeStamp );
	return ( diff > TimeOutDuration );

}


Message::Message(Type type, Action action, DataType dataType, std::string ID, std::string data)
{
	this->m_type = type;
	this->m_action = action;
	this->m_DataType = dataType;
	this->ID = ID;
	this->data = data;
}

std::string Message::Tostring(std::string field)
{
	if(field == "m_MsgType")
	{
		if(m_type == REQ)
		{
			return "REQ";
		}
		else if(m_type == RES)
		{
			return "RES";
		}
		else if(m_type == ACK)
		{
			return "ACK";
		}
	}
	else if(field == "m_action")
	{
		if(m_action == BIND)
		{
			return "BIND";
		}
		else if(m_action == PULL)
		{
			return "PULL";
		}
		else if(m_action == PUSH)
		{
			return "PUSH";
		}
		else if(m_action == SUBMIT)
		{
			return "SUBMIT";
		}
		else if(m_action == ENTITY)
		{
			return "ENTIIY";
		}
	}
	else if(field == "m_DataType")
	{
		if(m_DataType == Temp)
		{
			return "Temp";
		}
		else if(m_DataType == Traffic)
		{
			return "Traffic";
		}
		else if(m_DataType == CSS)
		{
			return "CSS";
		}
	}
	return "Unknown";
}

typedef boost::shared_ptr< Message > MsgPointer;

#endif
//int main() {
//	Message message(REQ, SUBMIT, CSS, "8964", "Jambalaya");
//	std::cout << message.Tostring("m_MsgType");
//}
